 using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Virtual_Mouse
{
    /// <summary>
    /// Klasa symulująca myszkę
    /// </summary>
    class MouseSimulator
    {
        private Point mousePosition;

        public MouseSimulator()
        {
            mousePosition = new Point();
            GetCursorPos(out mousePosition);
        }

        /// <summary>
        /// Przesuwa kursor w podane miejsce
        /// </summary>
        /// <param name="point">Punkt, w którym ma się znaleźć kursor</param>
        public void SetCursorPosition(Point point)
        {
            Point temp = mousePosition;
            mousePosition = point;

            bool test = SetCursorPos(point.X, point.Y);

            if (test == false)
            {
                MessageBox.Show("Błąd podczas ustawiania kursora");
                mousePosition = temp;
            }
        }


        /// <summary>
        /// Przesuwa kursor o podany wektor
        /// </summary>
        /// <param name="dx">Przesunięcie na OX</param>
        /// <param name="dy">Przesunięcie na OY</param>
        public void MoveCursor(int dx, int dy)
        {
            SetCursorPosition(new Point(mousePosition.X + dx, mousePosition.Y + dy));
        }

        public void MoveCursor(Point p, Point windowSize)
        {
            var border = new Point(windowSize.X / 3 - 10, windowSize.Y / 3 - 10);
            const int d = 10;

            if (p.X < border.X)
            {
                MoveCursor(-d, 0);
            }
            else if (p.X > (windowSize.X - border.X))
            {
                MoveCursor(d, 0);
            }

            if (p.Y < border.Y)
            {
                MoveCursor(0, -d);
            }
            else if (p.Y > (windowSize.Y - border.Y))
            {
                MoveCursor(0, d);
            }
        }

        public void ClickLeftMouseButton(int x, int y)
        {
            INPUT mouseInput = new INPUT();
            mouseInput.type = SendInputEventType.InputMouse;
            mouseInput.mkhi.mi.dx = x;
            mouseInput.mkhi.mi.dy = y;
            mouseInput.mkhi.mi.mouseData = 0;

            //getting current cursor location
            Point p;
            if (GetCursorPos(out p))
                SetCursorPos(x, y);


            mouseInput.mkhi.mi.dwFlags = MouseEventFlags.LeftDown;
            SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));

            mouseInput.mkhi.mi.dwFlags = MouseEventFlags.LeftUp;
            SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));

            //returning cursor to previous position
            SetCursorPos(p.X, p.Y);
        }    

        public void ClickLeftMouseButton()
        {
            ClickLeftMouseButton(this.mousePosition.X, this.mousePosition.Y);
        }

        /// <summary>
        /// Symuluje wciskanie przycisku, ruchy myszką oraz kliknięcia
        /// </summary>
        /// <param name="nInputs">Ilość struktur pInputs</param>
        /// <param name="pInputs">Tablica symulowanych działań</param>
        /// <param name="cbSize">Rozmiar struktury pInput</param>
        /// <returns>Liczba działań, które zostały zasymulowane poprawnie</returns>
        [DllImport("user32.dll", SetLastError = true)]
        private static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

        /// <summary>
        /// Ustawia kursor w podanej pozycji
        /// </summary>
        /// <param name="X">Miejsce na osi X</param>
        /// <param name="Y">Miejsce na osi Y</param>
        /// <returns>True jeśli metoda wykonała się poprawnie, False w przeciwnym wypadku</returns>
        [DllImport("user32.dll")]
        private static extern bool SetCursorPos(int X, int Y);

        /// <summary>
        /// Zwraca pozycję kursora
        /// </summary>
        /// <param name="lpPoint">Parametr, w którym zostanie zwrócona pozycja kursora</param>
        /// <returns>True jeśli metoda wykonała się poprawnie, False w przeciwnym wypadku</returns>
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetCursorPos(out Point lpPoint);

        [StructLayout(LayoutKind.Sequential)]
        struct INPUT
        {
            public SendInputEventType type;
            public MouseKeybdhardwareInputUnion mkhi;
        }

        [StructLayout(LayoutKind.Explicit)]
        struct MouseKeybdhardwareInputUnion
        {
            [FieldOffset(0)]
            public MouseInputData mi;

            [FieldOffset(0)]
            public KEYBDINPUT ki;

            [FieldOffset(0)]
            public HARDWAREINPUT hi;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct KEYBDINPUT
        {
            public ushort wVk;
            public ushort wScan;
            public uint dwFlags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct HARDWAREINPUT
        {
            public int uMsg;
            public short wParamL;
            public short wParamH;
        }

        struct MouseInputData
        {
            public int dx;
            public int dy;
            public uint mouseData;
            public MouseEventFlags dwFlags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        [Flags]
        enum MouseEventFlags : uint
        {
            Move = 0x0001,
            LeftDown = 0x0002,
            LeftUp = 0x0004,
            RightDown = 0x0008,
            RightUp = 0x0010,
            MiddleDown = 0x0020,
            MiddleUp = 0x0040,
            XDown = 0x0080,
            XUp = 0x0100,
            Wheel = 0x0800,
            VirtualDesk = 0x4000,
            Absolute = 0x8000
        }

        enum SendInputEventType : int
        {
            InputMouse,
            InputKeyboard,
            InputHardware
        }
    }
}
